﻿using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;

namespace O1.Kernel.IO.Storage
{
    [ContractVerification(true)]
    internal static class StorageExtensions
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool CheckConcurrency(this byte[] buffer, int offset, TransactionToken info, int cellId)
        {
            Contract.Requires(0 <= offset);
            Contract.Requires(info != null);
            Contract.Requires(buffer != null);
            Contract.Requires(buffer.Length >= Sz.CLR.Int64 + offset);

            var encodedTransactionId = buffer.ToUInt64(offset);
            if (!info.IsWriteVisible(encodedTransactionId))
            {
                info.AddNotice(encodedTransactionId, TransactionNotice.ReasonWriteConflict, cellId);
                return false;
            }

            return true;
        }

        public static unsafe ulong ToUInt64(this byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Sz.CLR.Int64 <= buffer.Length);

            fixed (byte* p = &buffer[offset])
            {
                return *((ulong*)p);
            }
        }

        public static unsafe void CopyBytes(this ulong value, byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Sz.CLR.Int64 <= buffer.Length);

            fixed (byte* p = &buffer[offset])
            {
                *((ulong*)p) = value;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte[] UpdatePreviousLink(this Linkage targetLink, int linkedId, byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Sz.CLR.Int64 < buffer.Length);

            new Linkage(linkedId, targetLink.NextId).Write(buffer, offset);
            return buffer;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte[] UpdateNextLink(this Linkage targetLink, int linkedId, byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Sz.CLR.Int64 < buffer.Length);

            new Linkage(targetLink.PreviousId, linkedId).Write(buffer, offset);
            return buffer;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte[] ExpandAndShift(this byte[] source, int recordSize)
        {
            Contract.Requires(source != null);
            Contract.Requires(recordSize > 0);
            Contract.Requires(recordSize <= source.Length);

            Contract.Ensures(Contract.Result<byte[]>() != null);
            Contract.Ensures(Contract.Result<byte[]>().Length == recordSize + source.Length);

            var target = new byte[recordSize + source.Length];
            Array.Copy(source, 0, target, recordSize, source.Length);
            return target;
        }
    }
}
